﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using MacomberMapSystem.Common.Database;
using System.Xml;
using MacomberMapSystem.Common.Internals;
using System.IO;
using Ionic.Zip;
using MacomberMapSystem.Common.Serialization;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.Threading;

namespace MacomberMapSystem.Common.Processing
{
    /// <summary>
    /// This class backs up the Oracle database
    /// </summary>
    public class CIM_DatabaseBackup: CIM_Processor
    {
        #region Imports
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern SafeFileHandle CreateNamedPipe(String pipeName, uint dwOpenMode, uint dwPipeMode, uint nMaxInstances, uint nOutBufferSize, uint nInBufferSize, uint nDefaultTimeOut, IntPtr lpSecurityAttributes);
        
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern SafeFileHandle CreateFile(String pipeName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplate);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern int ConnectNamedPipe(SafeFileHandle hNamedPipe, IntPtr lpOverlapped);
        #endregion

        #region Constants
        private const uint GENERIC_READ = (0x80000000);
        private const uint GENERIC_WRITE = (0x40000000);
        private const uint OPEN_EXISTING = 3;
        private const uint DUPLEX = (0x00000003);
        private const uint FILE_FLAG_OVERLAPPED = (0x40000000);
        private const int BUFFER_SIZE = 4096;
        #endregion
        /// <summary>
        /// 
        /// </summary>
        public override string ProcessName
        {
            get { return "Database backup"; }
        }

        /// <summary>
        /// Initialize a new database backup
        /// </summary>
        /// <param name="xElem"></param>
        /// <param name="NamedParameters"></param>
        /// <param name="Repository"></param>
        public CIM_DatabaseBackup(XmlElement xElem, Dictionary<String,object> NamedParameters, MM_Repository Repository)
            : base(xElem, NamedParameters, Repository)
        { }

        /// <summary>
        /// Execute our backup
        /// </summary>
        /// <param name="DbConn"></param>
        public override enumProcessState InitiateProcess(MM_Database_Connector DbConn)
        {
            //Set up our named pipe
            String PipeName = "\\\\.\\pipe\\OracleBackup-SharepointFileName";
            SafeFileHandle clientPipeHandle = CreateNamedPipe(PipeName, DUPLEX | FILE_FLAG_OVERLAPPED, 0, 255, BUFFER_SIZE, BUFFER_SIZE, 0, IntPtr.Zero);

            //failed to create named pipe
            if (clientPipeHandle.IsInvalid)
                throw new InvalidOperationException("Unable to create named pipe " + PipeName);
            //else if (ConnectNamedPipe(clientPipeHandle, IntPtr.Zero) != 1)
             //   throw new InvalidOperationException("Unable to connect to named pipe " + PipeName);

            //Open and set up our ZIP file
            using (ZipOutputStream zS = new ZipOutputStream(Path.Combine(ProcessString(TargetFolder), ProcessString(TargetFileName) + ".zip")))
            {
                zS.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
                zS.Comment = "Exported by " + Environment.UserDomainName + "\\" + Environment.UserName + " at " + DateTime.Now.ToString();
                zS.ProvisionalAlternateEncoding = Encoding.UTF8;
                zS.UseUnicodeAsNecessary = true;
                zS.PutNextEntry(ProcessString(SharepointFileName) + ".dmp");

                //Now, create and kick off our process
                using (FileStream fS = new FileStream(clientPipeHandle, FileAccess.Read))
                using (Process pS = new Process())
                {
                    pS.StartInfo.UseShellExecute = false;
                    pS.StartInfo.RedirectStandardError = true;
                    pS.StartInfo.RedirectStandardInput = true;
                    pS.StartInfo.RedirectStandardOutput = true;
                    pS.OutputDataReceived += new DataReceivedEventHandler(pS_OutputDataReceived);
                    pS.StartInfo.FileName = "exp.exe";
                    pS.StartInfo.Arguments = String.Format("{0}/{1}@{2} CONSISTENT=Y FILE='{3}' DIRECT=Y OWNER={0} STATISTICS=NONE", DbConn.CurrentDatabase.UserName, DbConn.CurrentDatabase.Password, DbConn.CurrentDatabase.DataSource, PipeName);
                    pS.ErrorDataReceived += new DataReceivedEventHandler(ps_ErrorDataReceived);
                    pS.Start();
                    Thread.Sleep(1000);
                    while (!pS.HasExited)
                    {
                        int BytesRead;
                        byte[] inBytes = new byte[BUFFER_SIZE];
                        BytesRead = fS.Read(inBytes, 0, inBytes.Length);
                        zS.Write(inBytes, 0, BytesRead);
                    }
                }
                zS.Flush();
                zS.Close();
            }
            return enumProcessState.Completed;
        }

            


            /*
            //Build our process
            Process ps = new Process();
            ps.StartInfo.UseShellExecute = false;
            ps.StartInfo.RedirectStandardError=true;
            ps.StartInfo.RedirectStandardInput=true;
            ps.StartInfo.RedirectStandardOutput=true;
            ps.StartInfo.FileName = "exp.exe";
            
            XmlElement xElem = DbConn.CurrentDatabase;
            ps.StartInfo.Arguments = String.Format("{0}/{1}@{2} CONSISTENT=Y FILE='{3}' DIRECT=Y OWNER={0} STATISTICS=NONE", xElem.Attributes["UserName"].Value, MM_System_Interface.Decrypt( xElem.Attributes["Password"].Value), xElem.Attributes["DataSource"].Value, Path.Combine(ProcessString(SharepointFolder), ProcessString(SharepointFileName) + ".dmp"));

            //Kick off our process, logging everything
            ps.ErrorDataReceived+=new DataReceivedEventHandler(ps_ErrorDataReceived);
            ps.Start();
            base.ProcessLog.AppendLine(ps.StandardOutput.ReadToEnd());
            ps.WaitForExit();

            //Now, compress our results                        
            
            using (ZipFile OutZip = new ZipFile())
            {
                OutZip.AddFile(Path.Combine(ProcessString(SharepointFolder), ProcessString(SharepointFileName) + ".dmp"));
                OutZip.Save(Path.Combine(ProcessString(SharepointFolder), ProcessString(SharepointFileName) + ".zip"));
            }
                
                        
            //If everything went well, delete our dump file
            File.Delete(Path.Combine(ProcessString(SharepointFolder), ProcessString(SharepointFileName) + ".dmp"));                        
        }*/
        
        /// <summary>
        /// Handle error text
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ps_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            base.ProcessLog.AppendLine("ERROR: " + e.Data);
        }

        /// <summary>
        /// Handle output data coming in from the stream
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void pS_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            base.ProcessLog.AppendLine("INFO: " + e.Data);
            Console.WriteLine("INFO: " + e.Data);
        }
        
    }
}
